Repository navigation
feat(credentials): keyless Snowflake, Microsoft app-only and Google keyless, GitHub and Bitbucket tokens, DocuSign JWT - #9869
Conversation
…ntity federation A Snowflake connection can now trust Sim's OIDC issuer instead of holding a programmatic access token. Sim signs a short-lived token (audience snowflakecomputing.com) for the credential's own random subject; the customer creates a SERVICE user with WORKLOAD_IDENTITY = (TYPE = OIDC ISSUER SUBJECT). - Save verifies by minting a token and running the identity statement at the pinned account host through the guarded egress path; Snowflake's refusals keep the shared codes (invalid_credentials, site_not_found, provider_unavailable). Subjects bind to their creator like AWS/Databricks. - Runtime: the resolver returns authStyle 'snowflake-workload-identity' for a keyless credential, and the tools and selectors send Bearer WIF.OIDC.<token> with the WORKLOAD_IDENTITY_FEDERATION token type. authStyle and domain are authoritative on every Snowflake tool, so a caller can no longer choose the token type or steer the account host of a saved credential. - PAT credentials resolve and verify exactly as before. - Connect dialog offers the keyless service user with the CREATE USER statement; it falls back to the token dialog where keyless is unavailable. - Docs: Snowflake connections page with keyless setup, troubleshooting, and the 60-minute Snowflake key cache on signing-key rotation.
…nd reopen keyless reconnects as keyless Cortex Analyst's "run the SQL" went through a nested snowflake_execute_sql call carrying the already-resolved token and host. With authStyle and domain now authoritative, the executor cleared both before the nested call, so it failed for every credential (and a keyless token would have been sent as a PAT). The generated SQL now runs as one guarded statement request built from the parent call's credential-derived host, token, and token type, through the same requestTarget egress the tool transport uses, with redirects refused. The Snowflake connect dialog opened every reconnect on the programmatic access token. The workload-identity prepare call now takes the reconnected credential and reports whether it is keyless (only to an admin of that credential in the same workspace), and the dialog opens on that mode. Docs: the 60-minute wait applies before signing with a new key; removing the old key needs 10 minutes with keyless Snowflake connections.
The generated SQL used to run as a nested tool call, which inherited the run's abort signal; running it directly dropped that. postProcess now receives the executor's effective abort signal as an optional fourth argument (the same signal the tool's own request used), and Cortex Analyst forwards it to the DNS check, the guarded fetch, and the response read. Other tools' postProcess hooks are unchanged.
…access tokens GitHub: a saved personal access token credential (fine-grained or classic), verified against api.github.com/user with the tools' own headers. GitHub's workflow block authenticates with an inline token rather than OAuth, so it follows the token-block pattern: github_v3 is the credential-only successor of github_v2, which stays exactly as it was, marked legacy and hidden from the toolbar. Every GitHub tool declares the optional saved-token policy; inline tokens keep working. The GitHub App installation credential (Search) is a separate service and is untouched: the Assistant still projects GitHub tools onto the person's connected GitHub account, and Search policy now follows the visible GitHub block (github_v3). Bitbucket: a workspace, project, or repository access token as a second option next to OAuth (the Linear pattern), verified by listing one repository of the typed workspace on api.bitbucket.org; the slug is validated so it cannot steer the path. List Workspaces needs a signed-in user, so it now refuses a saved access token before any request. Also: isHostNotFound no longer lets an outer error code mask ENOTFOUND on its cause.
…et tokens - The GitHub token service is `github-token`, so it no longer shares an id with the GitHub sign-in provider: parseProvider and getProviderIdFromServiceId answer for sign-in exactly as before, and a token-only service is never marked connected from a sign-in account whose id prefixes it. - Suggested connect rows offer each integration once and count it connected when any of its services' OAuth or service-account credentials is saved (GitHub's Search App and saved tokens share one integration). - Assistant GitHub calls bind through the connected-account policy the Assistant projects (the person's github-repositories OAuth account) in the token resolver too, on both the ordinary and managed paths; workflow runs and saved workspace tokens are unaffected. - Bitbucket verification reads one repository (`workspace/repository`), the request Atlassian documents for workspace, project, and repository access tokens alike; listing a workspace is not documented for repository tokens. - prepare-workload-identity test uses the central credentials-access mock.
A saved DocuSign credential that authenticates with JWT Grant (an integration key, an impersonated user ID, the app's RSA private key, and an environment), offered as a second option beside the existing OAuth connection, which is unchanged. The minter signs an RS256 assertion (iss = integration key, sub = user, aud = the environment's authentication host without a scheme, scope "signature impersonation"), exchanges it at that host's /oauth/token, then reads /oauth/userinfo on the same host to bind the pinned account (or the user's default) and returns its eSignature REST base. Both hosts come only from the stored environment; a base_uri outside that environment's docusign.net shape is never trusted with the token. GUIDs and the key are checked before any request, and tokens are re-minted about 15 minutes before they expire, as DocuSign advises. DocuSign tools take the account base as an authoritative credential param, so a caller can never supply it, and DocuSignClient skips its deployment-host userinfo lookup when one is bound. OAuth tokens resolve exactly as before. Save-time failures map to consent_required (the connect modal shows the exact consent URL for the key and environment) and account_not_found (a pinned account the user does not belong to), alongside the shared invalid_credentials and provider_unavailable codes.
- The DocuSign knowledge connector now honours a JWT Grant credential's bound account. The resolved token's instanceUrl reaches the connector as credentialInstanceUrl in the sync context (seeded on every path through syncContextForToken). The connector re-validates it with the same parser as DocuSignClient, lists and fetches from that account in the credential's environment, links envelopes to that environment's web app, and validates without asking the deployment host. A seeded value of the wrong shape fails closed. OAuth syncs are unchanged. - RSA keys under 2048 bits are refused as invalid_credentials with copy that names the size, instead of jose's raw TypeError. - A short expires_in no longer makes every call re-mint: the cached lifetime floors at six minutes while staying a minute under the real expiry. - The connect modal shows the consent URL as a link with a copy button, and account_not_found says "pinned account" only when one was entered.
… SharePoint, Dataverse, and Dynamics 365 A saved Microsoft app credential (OAuth 2.0 client credentials) as an additional option next to Microsoft OAuth, which is unchanged. One app registration serves the four services whose tools run without a signed-in user; Outlook, OneDrive, Excel, Word, Planner, and Teams never offer it and token resolution refuses it for them (provider mismatch). - Modes: client secret; certificate (PS256 client assertion with the certificate's x5t#S256 thumbprint, aud = the tenant token endpoint, 5-minute lifetime; key/certificate pairing, RSA size, and expiry checked before any request); keyless federated credential (a Sim workload-identity token for the credential's own subject, audience api://AzureADTokenExchange, subject bound to the saving user, refused if the app also accepts an unheld subject). - The token resource comes from the tool's service, passed from the server-validated tool policy: Graph for Entra ID and SharePoint, the stored environment for Dataverse and Dynamics 365. Any other service is refused. - The environment URL is stored on the credential (public-cloud Dynamics hosts, https root only). Dataverse tools take credentialType and instanceUrl as authoritative params; an app credential's request goes only to its own environment and a block environment that names another is refused. OAuth requests keep the block environment exactly as before. Dynamics 365 already matched instanceUrl against the block environment. - Save check: a Graph token reading the app's own service principal (no permission needed), plus Dataverse WhoAmI when an environment is set. Entra AADSTS codes map to actionable copy; runtime failures keep the shared invalid_credentials / provider_unavailable split. Tokens cache per credential, resource, and stored-secret version in an LRU. - Azure AD Reset password refuses app-only credentials: Graph does not support application permissions for resetPassword. - Connect dialog with tenant ID, client ID, environment URL, and the issuer/audience/subject to paste; docs page with per-mode setup.
…ntials - SharePoint selectors (workspace and organization paths) and knowledge connectors now pass the service they resolve for (the selector's own serviceId, the connector's declared auth provider), so a Microsoft app credential browses SharePoint with a Graph token instead of being refused. The resolution option is renamed from toolService to service; it is only ever set from server-side policy. - AADSTS700211 and 700212 are no longer mapped: Microsoft documents only 70021 and 700213 for federated credential mismatches. Other keyless refusals still name the mismatch. - DocuSign minter test mocks carry account_name, and the connector access-token test narrows its tokens with a guard instead of non-null assertions.
…tity Federation A keyless mode of the existing Google service account credential (google-service-account) for Google Cloud APIs. JSON-key credentials, their connect flow, and their token minting are unchanged. - Flow: Sim signs a short-lived token for the credential's own subject with aud = the customer's provider resource name (https://iam.googleapis.com/projects/<number>/locations/global/ workloadIdentityPools/<pool>/providers/<provider>), exchanges it at sts.googleapis.com/v1/token (token-exchange, jwt subject token, cloud-platform scope, audience //iam.googleapis.com/...), then calls iamcredentials generateAccessToken for the service account with the tool's scopes and a 1h lifetime. Impersonation rather than direct federated access: some Cloud APIs reject federated tokens, and the service account keeps the same identity and roles a JSON key uses. - Inputs validated against Google's documented formats before any request: numeric project number, pool/provider IDs (4-32 [a-z0-9-], no gcp-), and a user-managed *.iam.gserviceaccount.com email, so nothing can steer the STS audience or IAM URL. Requests never follow redirects. - Scope: refuses domain-wide delegation (an impersonated user) and any service but BigQuery and Vertex AI before reading further or contacting Google, with a new shared unsupported_use code and copy pointing to a JSON key. Workspace blocks never offer the mode. - Save check: STS exchange, impersonation, and tokeninfo (no IAM role or enabled API needed), plus a probe that a subject no credential holds cannot impersonate the service account. Subject bound to the saving user with the signed proof; kept across reconnects. - STS/IAM failures map to actionable copy (provider settings, issuer reachability, the exact workloadIdentityUser binding) without Google's descriptions or any token; runtime keeps the invalid_credentials / provider_unavailable split. Tokens cache per credential, secret version, and scopes in an LRU. - Per-credential audiences: audiences.ts gains google without a fixed audience; the setup response omits audience for it and reports a keyless reconnect. Connect dialog shows the issuer, audience, subject, and filled-in gcloud commands (pool, OIDC provider with attribute condition on the subject, IAM binding); docs section added.
…d credentials - Drop the tokeninfo check: a token from generateAccessToken already proves the provider accepted Sim's token and the impersonation binding, and tokeninfo puts a live token in a query string. - Test that saving fails closed when the stray-subject pin probe hits an STS outage or an unreachable IAM endpoint. - Pass --project=<project number> on all three gcloud setup commands, drop the unused Resource Manager API from the docs, and raise the shared validation error for an empty scope list.
…ubjects unique The subject-uniqueness scan from #9866 covered only AWS and Databricks. It now also scans Snowflake, Microsoft app, and Google service account credentials, so no two live credentials of an owner hold one keyless subject. Their keyed secrets that carry no config (a Google JSON key, a Snowflake programmatic access token) hold no subject and no longer read as unreadable. The three keyless save paths report a newly bound subject so create checks it.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 39921753 | Triggered | Generic Private Key | 1237119 | apps/sim/lib/credentials/microsoft-app-service-account.server.test.ts | View secret |
| 39921752 | Triggered | Generic High Entropy Secret | 41caf90 | apps/sim/lib/credentials/google-workload-identity.server.test.ts | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
[High impact] The changes reviewed appear safe to merge, with no new actionable issues found. SummaryAdds saved-token, app-only, and keyless connections for Snowflake, Microsoft, Google Cloud, GitHub, Bitbucket, and DocuSign.
|
There was a problem hiding this comment.
All reported issues were addressed across 241 files
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
- Knowledge connector tests expect the service now passed with Google service-account token requests; a new test proves a JSON-key mint signs the same request with or without it. - Re-record the tool-registry-boundary baseline: the keyless Snowflake, Microsoft app, and Google modules add 5 modules behind credential-service, which tipped credit-usage past its allowance on top of staging's drift. - Docs compatibility baseline: record the new code examples in the Google and Snowflake service account pages. - Generate the Microsoft certificate test fixture at run time instead of committing a PEM key and certificate. - A Microsoft app credential reconnect opens on keyless only when the stored credential is federated (the prepare step now reports it), like Snowflake; new connections still default to keyless. - TSDoc on the Google project number, pool/provider ID, and service account email validators.
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 244 files
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
…ontext Reconnecting a keyless Google service account from a Workspace integration (Gmail, Drive, ...) offered the keyless form, saving a credential that integration can never use. The dialog now offers keyless only for a Google Cloud service (new or reconnect) or on the credential's own page; from a Workspace integration it explains the credential works only with BigQuery and Vertex AI and does not save. The chat connect control passes its service so it gets the same gate.
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 246 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 246 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
Brings in #9869 (keyless Snowflake, Microsoft app-only and Google keyless credentials, GitHub and Bitbucket tokens, DocuSign JWT Grant). - Legacy files this branch deleted (credential-selector, integration-block-detail, suggested-actions with its new connect-candidates, special-tags) stay deleted. Their one change, passing the OAuth service to the connect dialog so Google keyless is offered only for a Google Cloud service, is ported to the kept extension chat's special-tags; the Platform ports follow separately. - The new Snowflake, Microsoft app and Google keyless dialogs move to components/integrations/connect-service-account-modal on emcn-legacy, with field hints folded into labels; DocuSign's descriptor hints fold the same way. - useOAuthConnections stays deleted (only the deleted suggested actions read it). - The connect modal keeps this branch's removal of credentialDescription. - AWS and Databricks trust snippets wait for an audience, now optional in the workload-identity setup response; the Platform config form does the same. - Tool-registry baseline: three-way merge of the rows, the higher value where both sides grew.
…onnects a provider Every connection method staging's #9869 added is now a choice in the Platform's existing setup dialogs, as AWS and Databricks are: an Authentication select, each choice's own fields, and for keyless sign-in the issuer, audience, subject and setup snippet read from the workload-identity setup. - Snowflake: a config descriptor offers the keyless service user (with its CREATE USER statement) beside the programmatic access token, which still submits apiToken and domain; a saved Search account keeps the token form. Labelled "Add Snowflake connection". - Microsoft app credential (Entra ID, SharePoint, Dataverse, Dynamics 365): a config descriptor with federated (keyless), client secret and certificate. - Google keyless: the Google dialog offers Workload Identity Federation beside the JSON key, only for a Google Cloud service (BigQuery, Vertex AI). The OAuth service reaches it from the catalog connection on the Integrations page and from the block's serviceId in the workflow editor's credential picker. - A reconnect of Snowflake, Microsoft or Google opens on keyless only when the setup reports the stored credential is keyless; new Google credentials start on the JSON key. - GitHub and Bitbucket tokens and DocuSign JWT Grant come through the existing token and client-credential forms; GitHub and Bitbucket follow GitLab's service-account token (personal_token stays GitLab-only on the server). - The catalog generator lists the new methods and carries each service account's OAuth service; the field renderer is shared by the key form and the Google dialog.
Summary
More ways to connect popular integrations without a person's OAuth session: saved tokens, app-only logins, and keyless OIDC through Sim's workload-identity issuer. Every option sits next to the integration's existing auth, and existing credentials, blocks and workflows are unchanged.
Bearer WIF.OIDC.…with the WIF token-type header), and the connect dialog shows the exactCREATE USER … WORKLOAD_IDENTITYstatement. Access-token connections send the same requests as before.github_v3block runs on a saved token, checked atGET /user.github_v2becomes legacy and keeps working. The token service isgithub-token, so GitHub sign-in accounts behave exactly as before.workspace/repository, the request Atlassian documents for every token type./oauth/userinfo. If DocuSign asks for consent, the dialog shows the consent URL.demo.docusign.netfor demo,*.docusign.netfor production.x5t#S256), or keyless through a federated identity credential (api://AzureADTokenExchange).WhoAmIwhen an environment is set.gcloudcommands, including the attribute condition that pins the subject.Type of Change
Testing
bun run lint,bun run --cwd apps/sim type-check.check-block-registry.ts origin/staging, including the saved-credential contract.check:test-patterns,check:test-types,check:explicit-any,check:tool-metadata,check:docs,check:docs-manifest,check:deployment-config,check:block-successors,check:integration-catalog,check:api-validation:strict.Checklist
test-auditauthoring gate)